/**
 * HEIG-VD
 * MCR - Projet "Pandemie"
 * Auteurs: Thierry Forchelet, Louis Jaquier, Grégory Moinat, Julien Rebetez
 */

package gui;

import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import app.Logger;
import app.World;

import ui.AbstractWorldView;
import visitable.Chicken;
import visitable.ConnectionManager;
import visitable.Creature;
import visitable.CreatureConnection;
import visitable.Human;
import visitable.Pig;
import visitable.SpreadManager;
import visitors.Visitor;

/**
 * Interface utilisateur de l'application
 */
public class WorldView extends AbstractWorldView {  
  private JFrame window;
  private JMenuBar menuBar;
  private JMenu menuAction;
  private JMenuItem menuItemNew;
  private JMenuItem menuItemQuit;
  private JSplitPane container;
  private CreaturePanel mainPanel;
  private JEditorPane logView;
  private JScrollPane logScrollPane;
  private HashMap<Creature, CreatureView> creatureToView = new HashMap<Creature, CreatureView>();
  
  /**
   * On spécialise un panel qui va contenir toutes les créatures (ainsi que leurs connexions) du monde
   */
  private class CreaturePanel extends JPanel {
    //La liste des connexions à afficher
    private List<ConnectionView> connectionViews = new ArrayList<ConnectionView>();
    
    /**
     * Permet d'ajouter une connexion qui sera affichée 
     * @param v la connexion à ajouter
     */
    protected void addConnectionView (ConnectionView v) {
      connectionViews.add(v);
    }
    
    @Override
    public void paint(Graphics g) {
      g.setClip(null);
      super.paint(g);
      
      Graphics2D g2d = (Graphics2D)g;
      g2d.setStroke(new BasicStroke(4.0f));
      for (ConnectionView c: connectionViews)
        c.draw(g2d);
    }
  }

  /**
   * Création d'une nouvelle interface
   * @param v le monde à visualiser
   */
  public WorldView(World v) {
    super(v);
    initComponents();
    createCreaturesViews(v);
    mainPanel.validate();
  }
  
  public void repaint () {
    mainPanel.repaint();
  }

  /**
   * Initialisation des composants swing
   */
  private void initComponents() {
    window = new JFrame("Pandemie");

    // Menu
    menuBar = new JMenuBar();
    menuAction = new JMenu("Action");
    menuItemNew = new JMenuItem("Nouveau monde...");
    menuItemQuit = new JMenuItem("Quitter");
    menuAction.add(menuItemNew);
    menuAction.add(menuItemQuit);
    menuBar.add(menuAction);
    window.setJMenuBar(menuBar);

    // Actions du menu nouveau
    menuItemNew.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        newWorld();
      }
    });

    // Actions du menu quitter
    menuItemQuit.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        System.exit(0);
      }
    });

    // Main container (contient le panneau de la population et le panneau du log)
    container = new JSplitPane();
    container.setOrientation(JSplitPane.VERTICAL_SPLIT);
    container.setDividerLocation(380);
    container.setResizeWeight(1.0);
    window.getContentPane().add(container);

    // Main panel
    mainPanel = new CreaturePanel();
    mainPanel.setPreferredSize(new Dimension(640, 380));
    mainPanel.setLayout(new GridLayout(world.getGridSize(), world.getGridSize()));
    container.setTopComponent(mainPanel);

    // Log view
    logScrollPane = new JScrollPane();
    logView = new JEditorPane();
    // logView.setContentType("text/html");
    logView.setEditable(false);
    logView.setPreferredSize(new Dimension(640, 150));
    logScrollPane.setViewportView(logView);
    container.setBottomComponent(logScrollPane);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    window.setPreferredSize(new Dimension(640, 530));
    window.pack();
    window.setVisible(true);
  }
  
  /**
   * http://rmarcus.wordpress.com/2009/01/24/java-scrolling-a-jscrollpane-jeditorpane-to-the-bottom/
   * Fait defiler la scrollPane pour le log jusqu'en bas
   */
  private void scrollPaneToBottom() {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        logScrollPane.getVerticalScrollBar().setValue(
        logScrollPane.getVerticalScrollBar().getMaximum());
      }
    });
  } 

  /**
   * Gestion lorsqu'une nouvelle ligne est ajoutée au journal
   */
  @Override
  protected void newLogLine(String newLine) {
    if (logView != null) {
      final String old = logView.getText();
      logView.setText(old + newLine + "\n");
      scrollPaneToBottom();
    }
    System.out.println(newLine);
  }

  /**
   * Un visiteur qui crée les vues pour les créatures
   */
  private class CreatureViewCreator extends Visitor {
    private void addView(CreatureView v, Creature c) {
      creatureToView.put(c, v);
      mainPanel.add(v);
    }

    @Override
    public void visit(Human human) {
      addView(new HumanView(human, WorldView.this), human);
    }

    @Override
    public void visit(Pig pig) {
      addView(new PigView(pig, WorldView.this), pig);
    }

    @Override
    public void visit(Chicken chicken) {
      addView(new ChickenView(chicken, WorldView.this), chicken);
    }
  }

  private void createCreaturesViews(World world) {
    Vector<Creature> creatures = new Vector<Creature>(world.getCreatures());
    CreatureViewCreator viewCreator = new CreatureViewCreator();
    for (Creature c : creatures)
      c.accept(viewCreator);
    
    createConnectionViews();
  }

  /**
   * Permet de créer un nouveau monde et spécifier sa taille
   */
  private void newWorld() {
    final JDialog newWorldDialog = new JDialog(window, true);
    JLabel labelGridSize = new JLabel("Taille de la grille:");
    final JTextField txtGridSize = new JTextField(Integer.toString(world
        .getGridSize()));
    JButton okButton = new JButton("Ok");

    // Propriétés du dialogue
    newWorldDialog.setTitle("Nouveau monde");
    newWorldDialog.setSize(new Dimension(300, 80));
    newWorldDialog.setLocation(window.getX() + window.getWidth() / 2
        - newWorldDialog.getWidth() / 2, window.getY() + window.getHeight() / 2
        - newWorldDialog.getHeight() / 2);
    newWorldDialog.setResizable(false);
    newWorldDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
    newWorldDialog.setLayout(new FlowLayout());

    // Action du bouton Ok
    okButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        // Création du nouveau monde si la valeur est bien un nombre
        try {
          int gridSize = Integer.parseInt(txtGridSize.getText());
          // Vérification de la taille spécifiée
          if (gridSize < 1)
            JOptionPane.showMessageDialog(newWorldDialog,
                "Veuillez spécifier un nombre entier supérieur à 0", "Erreur",
                JOptionPane.ERROR_MESSAGE);
          else {
            ConnectionManager.getInstance().clearConnections();
            SpreadManager.getInstance().clear();
            logView.setText("");
            setWorld(new World(gridSize));
            mainPanel = new CreaturePanel();
            mainPanel.setPreferredSize(new Dimension(640, 380));
            container.setDividerLocation(0.8);
            mainPanel.setLayout(new GridLayout(world.getGridSize(), world
                .getGridSize()));

            createCreaturesViews(world);
            container.setTopComponent(mainPanel);
            newWorldDialog.dispose();
          }

        } catch (NumberFormatException e1) {
          JOptionPane.showMessageDialog(newWorldDialog,
              "Veuillez spécifier un nombre entier supérieur à 0", "Erreur",
              JOptionPane.ERROR_MESSAGE);
        }

      }
    });

    // Mise en place des composants
    newWorldDialog.getContentPane().add(labelGridSize);
    txtGridSize.setPreferredSize(new Dimension(100, 20));
    newWorldDialog.getContentPane().add(txtGridSize);
    newWorldDialog.getContentPane().add(okButton);
    newWorldDialog.getRootPane().setDefaultButton(okButton);

    newWorldDialog.setVisible(true);
  }
  
  /**
   * Permet d'afficher les connections
   */
  private void createConnectionViews() {
     Set<CreatureConnection> connections = ConnectionManager.getInstance().getConnectionsSet();
     Logger.getInstance().log(this, "Creating " + connections.size() + " connections");
     for (CreatureConnection conn: connections) {        
       final ConnectionView connView = new ConnectionView(conn, 
                                             creatureToView.get(conn.getFirstCreature()),
                                             creatureToView.get(conn.getSecondCreature()),
                                             this);
       //mainPanel.add(connView.getNode());
       mainPanel.addConnectionView(connView);
     }
   }
}
